/* 
CPSC 323 - Lexical Analysis
Katie Soto
Chelsea Gunderson
9/2011
*/

#ifndef LEXICALANALYZER_H
#define LEXICALANALYZER_H

#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>

using namespace std;

//********VARIABLES:*************
	struct results
	{
			string lexeme;
			string token;
	};
	results resultsStruct;
	const int num_seps = 12;

	string keywords[13]= { "function", "int", "boolean", "real", "if", "endif", "else", "return", "print", "scan", "while", "true", "false"};
	char operators[11] = {'+', '-', '<', ':', '=', '/', '>', '<', '=', '*', '/'};
		//^ some operators that are two characters: <= := /= => these are taken care 
		//of in main.cpp
	char seperators[num_seps] = {'#', '(', ')', ',', ':', '{', '}', '[', ']', ';', ' '};
//**FIX FROM PROJECT 1** "." is not a seperator. '.' taken out of seperators[]		
		//^spacebar is considered a seperator because "function bb" are 2 items, 
		//where "functionbb" is not..however when spacebars are identified, they
		//will be ignored (the token and lexeme of it, that it)
	
	//RE for identifiers: (l_|l)+
	int FSM_identifiers[4][3]={{0, 1, 3},
								{1, 1, 2},
								{2, 1, 3},
								{3, 3, 3}};
	//RE for ints and reals: (d+)|(d*.d+)
	int FSM_integers_and_reals[5][3]={      {0, 1, 2},
											{1, 1, 2},
											{2, 3, 4},
											{3, 3, 4},
											{4, 4, 4}       };

	long length;
	char *buffer;

//********FUNCTIONS:*************
	bool is_keyword( string checkString );	//is the string a keyword? true/false
	bool is_operator( char checkChar );		//is the character an operator? true/false
	bool is_seperator( char checkChar );	//is the character a seperator? true/false
	int char_to_col_id(char w);				//what column do I go to next in the id 
												//FSM using this character?
	bool is_identifier( string w );			//is the string an identifier? true/false
	int char_to_col_int(char w);			//what column do I go to next in the 
												//int/real FSM using this character?
	int is_int_or_real( string w );			//is the string an integer or real? 1 for
												//int, 3 for real.

//********FUNCTION DEFINITIONS:*************
	//is_keyword:
	//-purpose: checks if the string passed in is contained in the keywords array
	//-input: string checkString, the string to check
	//-output: true/false
	bool is_keyword( string checkString )
	{ 
			for( int i = 0; i < 13; i++ )
			{
					if( checkString == keywords[i] )
					{
							return true;
					}
			}

			return false;
	}

	//is_operator:
	//-purpose: checks if the string passed in is contained in the operators array
	//-input: character checkChar, the character to check
	//-output: true/false
	bool is_operator( char checkChar )
	{ 
			for( int i = 0; i < 11; i++ )
			{
					if( checkChar == operators[i] )
					{
							return true;
					}
			}

			return false;
	}

	//is_seperator:
	//-purpose: checks if the string passed in is contained in the seperators array
	//-input: character checkChar, the character to check
	//-output: true/false
	bool is_seperator( char checkChar )
	{ 
			for( int i = 0; i <= num_seps; i++ )
			{
					if( checkChar == seperators[i] )
					{
							return true;
					}
			}

			return false;
	}

	//char_to_col: 
	//-purpose: maps the input character to the correct column for is_identifier
	//-input: the input character w
	//-output: the number of the column to go to
	int char_to_col_id(char w){
		/*this is our identifier table:
									0  1  2
									l  _
								0  {0, 1, 3},
								1  {1, 1, 2},
								2  {2, 1, 3},
								3  {3, 3, 3}  
		so if the input character is a letter, go to column 1, if the input 
			character is _, go to column 2!
		*/
		if(isalpha(w)){
			//column = 1;
			return 1;
		}else if(w=='_'){
			//column = 2;
			return 2;
		}else{
			cout<<"ERROR: Lexical error- identifier input is neither a letter nor an underscore."<<endl;
		}

	}

	//is_identifier
	//-purpose: given a string, this function go through the identifier finite 
	//			state machine. at the end of going through the FSM, if the "state"
	//			is in an "accepting state", then the string is a "corrent" identifier.
	//-input: string w to check if it's an identifier or not
	//-output: true (yes, it's a proper identifier) / false (no, do not accept)
	bool is_identifier( string w )
	{ 
			//go through the FSM FSM_identifiers
			//if in accepting state, return true.
			//note the function char_to_col(ch) returns the column number of the 
			// ch in the table
			int state = 0; //q0 = starting state = 0
			int column = 0;
			for(int i=0; i<w.length(); i++){
				column = char_to_col_id(w[i]);
				state = FSM_identifiers[state][column];
			}
			if(state == 1){	//if we are in an accepting state, in our case, 
								//1 and 2 are accepting states
				return true; //then accept the string as a proper identifier
			}else if(state == 2){
				return true;
			}else if(state == 3){
				return false;
			}else{
				cout<<"ERROR: Lexical error- id FSM ended in non-existant state "<<state;
				return false;
			}
	}

	
	//char_to_col_int: 
	//-purpose: maps the input character to the correct column for is_int_or_real
	//-input: input character w
	//-output: the number of the column to go to
	int char_to_col_int(char w){
		/*this is our int and reals table:
	int FSM_integers_and_reals[5][3]={		 0  1  2
												d  .
										0	{0, 1, 2},
										1	{1, 1, 2},
										2	{2, 3, 4},
										3	{3, 3, 4},
										4	{4, 4, 4}       };
		so if the input character is a digit, go to column 1, if the input 
			character is ., go to column 2!
		*/
		if(isdigit(w)){
			//column = 1;
			return 1;
		}else if(w=='.'){
			//column = 2;
			return 2;
		}else{
			cout<<"ERROR: Lexical error- number input is neither a digit nor a period."<<endl;
		}

	}


	//is_int_or_real:
	//-purpose: given a string, this function go through the integer/real 
	//			finite state machine. at the end of going through the FSM,
	//			if the "state" is in an "integer accepting state", then the
	//			string is a "corrent" integer. If it is in the "real 
	//			accepting state", it is a "correct" real.
	//-input: string w to check if it's an integer/real or not
	//-output:	0 - do not accept
	//			1 - integer
	//			3 - real
	int is_int_or_real( string w )
	{ 
			//go through the FSM FSM_integers_and_reals
			//if in accepting state, return true.
		/*
	int FSM_integers_and_reals[5][3]={      {0, 1, 2},
											{1, 1, 2},
											{2, 3, 4},
											{3, 3, 4},
											{4, 4, 4}       };
		*/
    		int state = 0; //q0 = starting state = 0
			int column = 0;
			for(int i=0; i<w.length(); i++){
				column = char_to_col_int(w[i]);
				state = FSM_integers_and_reals[state][column];
			}
			if(state == 1){//if we are in an accepting state 1, you are an int
				return 1; //then accept the string as a proper integer
			}else if(state == 3){//if you are in an accepting state 3, you are a real
				return 3;
			}else{		//not an int or real.
				return 0;
			}    
		
		return 0;
	}

#endif